package cn.cqie.attendance.manager.service.impl;

import cn.afterturn.easypoi.excel.ExcelImportUtil;
import cn.afterturn.easypoi.excel.entity.ImportParams;
import cn.afterturn.easypoi.excel.entity.result.ExcelImportResult;
import cn.cqie.attendance.common.util.DateUtil;
import cn.cqie.attendance.common.util.Page;
import cn.cqie.attendance.common.util.PageBean;
import cn.cqie.attendance.manager.dao.AdminAttendanceDao;
import cn.cqie.attendance.manager.pojo.*;
import cn.cqie.attendance.manager.service.AdminAttendanceService;
import cn.cqie.attendance.manager.util.ValidateRecordUtil;
import com.github.pagehelper.PageHelper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.*;

/**
 * @author 697
 * @date 2021/7/8 10:50
 */
@Service
public class AdminAttendanceServiceImpl implements AdminAttendanceService {

    /**
     * 时间十分钟间隔
     */
    private final long time = 10 * 60 * 1000;
    @Autowired
    private AdminAttendanceDao adminAttendanceDao;

    /**
     * 查询所有考勤记录 可组合模糊查询
     *
     * @param adminAttendanceSerch
     * @param pageSize             每页的数量
     * @param pageNum              当前页
     * @return
     */
    @Override
    public PageBean<AdminAttendance> queryAllStudentAttendanceInfo(AdminAttendanceSerch adminAttendanceSerch, Integer pageSize, Integer pageNum) {

        if (pageNum == null || pageSize == null) {
            pageNum = 1;
            pageSize = 10;
        }
        PageHelper.startPage(pageNum, pageSize);
        List<AdminAttendance> list = adminAttendanceDao.queryAllStudentAttendanceInfo(adminAttendanceSerch);
        int total = list.size();
        if (null == list || total == 0) {
            return new PageBean<>(201, "error", list);
        }
        return new PageBean<>(200, "success", list);
    }


    /**
     * 查询所有门禁打卡记录 可组合模糊查询
     *
     * @param adminAttendancePunchSerch
     * @param pageSize                  每页的数量
     * @param pageNum                   当前页
     * @return
     */
    @Override
    public PageBean<AdminAttendancePunch> queryAllPunchInfo(AdminAttendancePunchSerch adminAttendancePunchSerch, Integer pageSize, Integer pageNum) {

        if (pageNum == null || pageSize == null) {
            pageNum = 1;
            pageSize = 10;
        }
        PageHelper.startPage(pageNum, pageSize);
        List<AdminAttendancePunch> list = adminAttendanceDao.queryAllPunchInfo(adminAttendancePunchSerch);
        int total = list.size();
        if (null == list || total == 0) {
            return new PageBean<>(201, "error", list);
        }
        return new PageBean<>(200, "success", list);
    }


    /**
     * 导入门禁数据
     *
     * @param adminAttendancePunchList
     * @return
     */
    @Override
    @Transactional
    @Async("threadPool")
    public void insertPunchDate(List<AdminAttendancePunch> adminAttendancePunchList) {
        for (AdminAttendancePunch punch : adminAttendancePunchList) {
            adminAttendanceDao.insertPunchRecord(punch);
        }
    }

    /**
     * 根据学号查询学生id
     *
     * @param stuNumber
     * @return
     */
    @Override
    public int queryStuIdByStuNum(String stuNumber) {
        return adminAttendanceDao.queryStuIdByStuNum(stuNumber);
    }

    /**
     * 根据学生id查询他的课程信息
     *
     * @param stuId
     * @return
     */
    @Override
    public List<AdminStuCourseLeaves> queryStudentCourseById(Integer stuId) {
        return adminAttendanceDao.queryStudentCourseById(stuId);
    }

    @Override
    public Page importPunchRecord(MultipartFile recordFile, Integer importMode) {
        String recordName = recordFile.getOriginalFilename();
        if (recordName.endsWith(".xls") || recordName.endsWith(".xlsx")) {

            ImportParams importParams = new ImportParams();
            importParams.setNeedVerify(true);
            importParams.setTitleRows(1);
            ExcelImportResult<AdminAttendancePunch> result = null;
            List<AdminAttendancePunch> record = null;

            try {
                result = ExcelImportUtil.importExcelMore(
                        recordFile.getInputStream(),
                        AdminAttendancePunch.class, importParams);

                record = new ArrayList<>(result.getList().size());
                Set<String> sheetSet = new HashSet<>();
                for (AdminAttendancePunch punch : result.getList()) {
                    if (sheetSet.contains(punch.toString())) {
                        if (importMode == 0) {
                            return new Page(999, "数据重复");
                        }
                        continue;
                    }
                    if (punch.getErrorMsg() == null && punch.getStuName() != null) {
                        punch.setPunchDate(DateUtil.formatDate("yyyy-MM-dd", punch.getPunchDate()));
                        punch.setTrueSignInTime(DateUtil.formatDate("HH:mm:ss", punch.getTrueSignInTime()));
                    }
                    record.add(punch);
                }
                if (!ValidateRecordUtil.validate(record)) {
                    throw new Exception("异常");
                }
            } catch (Exception e) {
                return new Page(500, "格式异常");
            }
            insertPunchDate(record);
            insertAttendance(record);
            return Page.SUCCESS;
        }
        return new Page(500, "上传文件只能是excel文件");
    }


    @Transactional
    @Async("threadPool")
    public void insertAttendance(List<AdminAttendancePunch> record) {
        // 另开一个线程去分析打卡数据和情况
        HashMap<Integer, List<AdminStuCourseLeaves>> stuCourseLeavesMap = new HashMap<>();
        HashMap<String, Integer> stuIdMap = new HashMap<>();
        record.forEach((punch) -> {
            String stuNumber = punch.getStuNumber();
            if (!stuIdMap.containsKey(stuNumber)) {
                Integer stuId = queryStuIdByStuNum(stuNumber);
                stuIdMap.put(stuNumber, stuId);
                stuCourseLeavesMap.put(stuId, queryStudentCourseById(stuId));
            }
        });
        stuCourseLeavesMap.forEach((k, v) -> {
            AdminAttendanceAdd adminAttendanceAdd = null;
            int status = 4; // 缺勤
            for (int i = 0; i < v.size(); i++) {
                AdminStuCourseLeaves asl = v.get(i);
                if (i == 0) {
                    adminAttendanceAdd = new AdminAttendanceAdd(k, asl.getClassRoom(), asl.getCourseId());
                }
                // 进入下一门课程, 将上一门课程的考勤情况写入
                if (i > 0 && v.get(i).getCourseId() != v.get(i - 1).getCourseId()) {
                    adminAttendanceAdd.setStatus(status);
                    adminAttendanceDao.insertPunchDate(adminAttendanceAdd);
                    status = 4;
                    adminAttendanceAdd = new AdminAttendanceAdd(k, asl.getClassRoom(), asl.getCourseId());
                }
                // 课程还没结束, 需要考勤
                if (asl.getCourseStatus() == 0) {

                    // 请假时间是否落在课程开始前十分钟内
                    if (asl.getStartTime().getTime() - time >= asl.getSignInTime().getTime()
                            && asl.getStartTime().getTime() <= asl.getSignInTime().getTime()) {
                        status = 2; // 早退
                        // 第一次插入
                        if (adminAttendanceAdd.getTrueSignInTime() == null) {
                            adminAttendanceAdd.setPunchDate(asl.getSignInTime());
                            adminAttendanceAdd.setTrueSignInTime(asl.getStartTime());
                        }
                        // 如果已经插入过一次, 并且这一次的请假的时间比上一次的时间要更接近上课时间
                        if (adminAttendanceAdd.getTrueSignInTime() != null
                                && DateUtil.formatDate("yyyy-MM-dd", asl.getStartTime())
                                .equals(DateUtil.formatDateToString("yyyy-MM-dd", adminAttendanceAdd.getPunchDate()))
                                && adminAttendanceAdd.getTrueSignInTime().getTime() < asl.getStartTime().getTime()) {
                            adminAttendanceAdd.setTrueSignInTime(asl.getStartTime());
                        }
                    }
                    // 请假时间是否落在课程结束后十分钟内
                    if (asl.getEndTime().getTime() >= asl.getSignOutTime().getTime()
                            && asl.getEndTime().getTime() <= asl.getSignOutTime().getTime() - time) {
                        if (status == 2) {
                            status = 3; // 请假
                        } else {
                            status = 1; // 迟到
                        }
                        // 第一次插入
                        if (adminAttendanceAdd.getTrueSignOutTime() == null) {
                            adminAttendanceAdd.setPunchDate(asl.getEndTime());
                            adminAttendanceAdd.setTrueSignOutTime(asl.getEndTime());
                        }
                        // 如果已经插入过一次, 并且这一次的请假的时间比上一次的时间要更接近下课时间
                        if (adminAttendanceAdd.getTrueSignOutTime() != null
                                && DateUtil.formatDate("yyyy-MM-dd", asl.getEndTime())
                                .equals(DateUtil.formatDateToString("yyyy-MM-dd", adminAttendanceAdd.getPunchDate()))
                                && adminAttendanceAdd.getTrueSignOutTime().getTime() > asl.getEndTime().getTime()) {
                            adminAttendanceAdd.setTrueSignOutTime(asl.getEndTime());
                        }
                    }
                    // 请假两次时间都落在正确范围内, 表示请假成功
                    if (status == 3) {
                        adminAttendanceAdd.setLeaveId(asl.getLeavesId());
                        continue;
                    }
                    // 如果请假的时间不能覆盖上课的考勤时间, 去打卡记录中寻找
                    if (status == 2) {
                        Date trueTime = searchPunchRecord(record, asl.getSignOutTime(), false);
                        if (trueTime != null) {
                            status = 3;
                            adminAttendanceAdd.setPunchDate(trueTime);
                            adminAttendanceAdd.setTrueSignOutTime(trueTime);
                        }
                    } else if (status == 1) {
                        Date trueTime = searchPunchRecord(record, asl.getSignInTime(), true);
                        if (trueTime != null) {
                            status = 3;
                            adminAttendanceAdd.setPunchDate(trueTime);
                            adminAttendanceAdd.setTrueSignInTime(trueTime);
                        }
                    } else {
                        Date trueSignOutTime = searchPunchRecord(record, asl.getSignOutTime(), false);
                        if (trueSignOutTime != null) {
                            status = 1;
                            adminAttendanceAdd.setPunchDate(trueSignOutTime);
                            adminAttendanceAdd.setTrueSignOutTime(trueSignOutTime);
                        }
                        Date trueSignInTime = searchPunchRecord(record, asl.getSignInTime(), true);
                        if (trueSignInTime != null) {
                            if (status == 1) {
                                status = 0; // 正常
                            } else {
                                status = 2;
                            }
                            adminAttendanceAdd.setPunchDate(trueSignInTime);
                            adminAttendanceAdd.setTrueSignInTime(trueSignInTime);
                        }
                    }
                }
            }
        });
    }

    /**
     * 在打卡中寻找有没有打过卡的记录
     *
     * @param record 打卡记录列表
     * @param date   需要打卡的时间
     * @param tag    true是课程前十分钟, false是课程后十分钟
     * @return 返回打卡的时间, null表示没有打卡
     */
    private Date searchPunchRecord(List<AdminAttendancePunch> record, Date date, boolean tag) {
        Long temp = null;
        for (AdminAttendancePunch punch : record) {
            long signTime = punch.getPunchDate().getTime() + punch.getTrueSignInTime().getTime();
            long standardTime = punch.getPunchDate().getTime() + date.getTime();
            if (tag) {
                if (signTime >= (standardTime - time) && signTime <= standardTime) {
                    if (temp == null) {
                        temp = signTime;
                    } else if (signTime > temp) {
                        temp = signTime;
                    }
                }
            } else {
                if (signTime <= (standardTime + time) && signTime >= standardTime) {
                    if (temp == null) {
                        temp = signTime;
                    } else if (signTime > temp) {
                        temp = signTime;
                    }
                }
            }
        }
        return temp == null ? null : new Date(temp);
    }

    /**
     * 导出门禁打卡记录
     * @return
     */
//    @Override
//    public List<AdminAttendancePunchPutOut> exportPunchDate() {
//        List<AdminAttendancePunchPutOut> list = adminAttendanceDao.exportPunchDate();
//        return list;
//    }
}
